package com.hanliy.shiro;

import com.hanliy.pojo.AdminEntity;
import com.hanliy.pojo.AdminPermissionEntity;
import com.hanliy.service.AdminPermissionService;
import com.hanliy.service.AdminService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.util.Assert;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.List;

/**
 * @author hanyulin
 * @apiNote 创建自定义得AdminRealm
 * Autowired注解需要配合Lazy注解使用，否则会导致这三个service相关的事务失效
 * @date 2021/11/6 14:06
 */
public class AdminAuthoriztionRealm extends AuthorizingRealm {

    @Autowired
    @Lazy
    private AdminService adminService;
    @Autowired
    @Lazy
    private AdminPermissionService adminPermissionService;

    /**
     * 授权
     *
     * @param principals 权限集合
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @author hanyulin
     * @date 2021/11/6 14:08
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 获取在认证的时候返回来的 第一个参数 admin
        AdminEntity currentAdmin = (AdminEntity) getAvailablePrincipal(principals);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 查询管理员对应的权限
        AdminPermissionEntity permission = adminPermissionService.queryPermissionByAdminId(currentAdmin.getId());
        if (permission.getPermission() == null) {
            return null;
        }
        info.addStringPermission(permission.getPermission());
        return info;
    }

    /**
     * 认证
     *
     * @param token : 认证Token，用来交互与数据库的用户信息
     * @return org.apache.shiro.authc.AuthenticationInfo
     * @author hanyulin
     * @date 2021/11/6 14:09
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        String username = userToken.getUsername();
        String password = new String(userToken.getPassword());

        if (!StringUtils.hasText(username)) {
            throw new AccountException("用户名不能为空");
        }

        DefaultSecurityManager securityManager = (DefaultSecurityManager)SecurityUtils.getSecurityManager();
        DefaultSessionManager sessionManager = (DefaultSessionManager) securityManager.getSessionManager();
        Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
        // 遍历session列表
        for (Session session : sessions) {
            /*
             * 用户登录成功，shiro会将用户名放到session的attribute中，key为DefaultSubjectContext.PRINCIPALS_SESSION_KEY
             * 就可以通过这个session里面的key找到登录的用户，再将用户的相关信息提取出来
             */
            Object object = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (null != object) {
                if (object instanceof SimplePrincipalCollection) {
                    /*
                     * PrincipalCollection是一个身份集合，因为我们可以在Shiro中同时配置多个Realm，所以呢身份信息可能就有多个
                     * getPrimaryPrincipal，有多个Principal，返回第一个（内部用Map存储的，所以可以认为返回任意一个）
                     * SimplePrincipalCollection 会合并多个Principal为一个PrincipalCollection
                     */
                    SimplePrincipalCollection spc = (SimplePrincipalCollection) object;
                    /*
                     * 将 身份信息 拷贝 为 AdminEntity
                     */
                    AdminEntity admin = new AdminEntity();
                    Object principal = spc.getPrimaryPrincipal();
                    BeanUtils.copyProperties(principal, admin);
                    /*
                     * 输入的username与存储在session里面的username 是否相等 ？  是：删除session
                     */
                    if (username.equals(admin.getUsername())) {
                        sessionManager.getSessionDAO().delete(session);
                    }
                }
            }
        }

        // 获取当前的session列表
        if (!StringUtils.hasText(password)) {
            throw new AccountException("密码不能为空");
        }

        // 数据库中获取
        List<AdminEntity> admins = adminService.queryAdminByName((username));

        Assert.state(admins.size() < 2, "同一个用户名存在两个账户");

        if (admins.size() == 0) {
            return null;
        }
        // 判断用户名是否正确
        AdminEntity admin = admins.get(0);
        if (!admin.getUsername().equals(userToken.getUsername())) {
            // 返回null 就会抛出 UnknownAccountException
            return null;
        }
        // shiro来验证密码
        //  new SimpleAuthenticationInfo  --> 数据库的用户信息(主要是用于在授权时候进行获取)  数据库的密码  盐–用于加密密码对比（可省略）  realm的名称
        return new SimpleAuthenticationInfo(admin, admin.getPassword(), getName());
    }
}
